//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<StyleText.cpp>
///	@path	~/src/gui/style/
///	@date	2008/10/21
///	@brief	.

#define	_FULLAPI
#include "config/config.h"

#include <CommCtrl.h>

#include "gui/style/StyleText.h"

namespace xeres {

	// StyleText
	StyleText::StyleText( void )
		: m_style( new CTextStyle )
		, m_arrangement( NULL )
		, m_count( -1 )
		, m_width( 0 )
	{
	}
	// StyleText
	StyleText::StyleText( const WString& text )
		: m_buffer( text )
		, m_style( new CTextStyle )
		, m_arrangement( NULL )
		, m_count( -1 )
		, m_width( 0 )
	{
	}
	// ~StyleText
	StyleText::~StyleText( void )
	{
		if( m_arrangement )
			free( m_arrangement );
	}

	// SetStyle
	void StyleText::SetStyle( Ref<CTextStyle> style )
	{
		if( style == m_style )
			return;

		// see if text needs to be relayouted
		bool need_layout = true;
		if( style.IsValid() && m_style.IsValid() )
		{
			if( !m_style->NeedRearrange( *style ) )
				need_layout = false;
		}
		if( need_layout )
		{
			m_count = -1;
			m_width = 0;
			if( m_arrangement )
			{
				free( m_arrangement );
				m_arrangement = NULL;
			}
		}
		m_style = style;
	}

	// SetString
	void StyleText::SetString( const WString& str )
	{
		if( str == m_buffer )
			return;

		m_buffer = str;
		m_count = -1;
		m_width = 0;
		if( m_arrangement )
		{
			free( m_arrangement );
			m_arrangement = NULL;
		}
	}

	// RelayoutText
	void StyleText::RelayoutText( void )
	{
		if( m_style.IsNull() )
		{
			// acquire parent's style.
			m_style = new CTextStyle();
		}
		m_count = (int)m_buffer.size();
		m_arrangement = (int*)malloc( m_count * sizeof(int) );
		m_style->ArrangeText( m_buffer.c_str() , m_count , m_arrangement , 0 , &m_width );
	}

	// DrawText
	void StyleText::DrawText( Composition& composition , const Rect<int>& place , const Rect<int>& bound , int offset /*= 0 */, int len /*= -1 */ )
	{
		if( m_buffer.size() == 0 )
			return;

		if( m_buffer.size() != m_count )
		{
			RelayoutText();
		}

		TextStyle& style = *m_style;
		PlacementStyle& placement = *m_style;

		int ascent = style.m_font->GetAscent();
		int descent = style.m_font->GetDescent();
		int start_x = place.left() , start_y = place.top();
		int text_height = style.m_font->GetFontHeight();
		int line_height = text_height + style.m_lineHeight;

		int pad_left = placement.m_padding_left == 0 ? placement.m_padding : placement.m_padding_left;
		int pad_top = placement.m_padding_top == 0 ? placement.m_padding : placement.m_padding_top;
		int pad_right = placement.m_padding_right == 0 ? placement.m_padding : placement.m_padding_right;
		int pad_bottom = placement.m_padding_bottom == 0 ? placement.m_padding : placement.m_padding_bottom;

		int total_width = pad_left + pad_right + m_width;
		int total_height = pad_top + pad_bottom + line_height;

		// horizontal alignment
		switch( placement.m_horizontal )
		{
		case PlacementStyle::LEFT:
			start_x += pad_left;
			break;

		case PlacementStyle::MIDDLE:
			start_x += place.width() / 2 - total_width / 2;
			break;

		case PlacementStyle::RIGHT:
			start_x = place.right() - total_width - pad_right;
			break;
		}

		// vertical alignment
		switch( placement.m_vertical )
		{
		case PlacementStyle::TOP:
			 start_y += pad_top;
			 break;

		case PlacementStyle::CENTER:
			start_y += place.height() / 2 - total_height / 2;
			break;

		case PlacementStyle::BOTTOM:
			start_y = place.bottom() - total_height - pad_bottom + descent;
			break;
		}

		start_x += style.m_textOffset.x;
		start_y += style.m_textOffset.y;

		// draw them by arrangement
		composition.painter().DrawText( style.m_textColor , style.m_font , m_buffer.c_str() , -1 , bound , Pair<int>( start_x , start_y ) , m_arrangement , &style , 0 );

		int end_x = m_width;
		switch( style.m_decoration )
		{
		case TextStyle::DECORATION_NONE:
			break;
		case TextStyle::BOTHLINE:
		case TextStyle::UNDERLINE:
			{
				int baseline = style.m_font->GetBaseline();
				composition.graphics().DrawLine( &Gdiplus::Pen( style.m_textColor.argb ) ,
					start_x , start_y + baseline , start_x + end_x , start_y + baseline );
			}
			if( style.m_decoration == TextStyle::UNDERLINE )
				break;
		case TextStyle::OVERLINE:
			{
				int leading = style.m_font->GetInternalLeading();
				composition.graphics().DrawLine( &Gdiplus::Pen( style.m_textColor.argb ) ,
					start_x , start_y + leading , start_x + end_x , start_y + leading );
			}
			break;
		case TextStyle::LINE_THROUGH:
			{
				int center = style.m_font->GetFontHeight() / 2;
				composition.graphics().DrawLine( &Gdiplus::Pen( style.m_textColor.argb ) ,
					start_x , start_y + center , start_x + end_x , start_y + center );
			}
			break;
		}
	}
	

	// StyleTextBlock
	StyleTextBlock::StyleTextBlock( void )
		: m_style( new CTextStyle )
		, m_size(0,0)
		, m_arrangement( NULL )
		, m_count( -1 )
		, m_width( 0 )
		, m_height( 0 )
	{
	}

	// StyleTextBlock
	StyleTextBlock::StyleTextBlock( const WString& text )
		: m_buffer( text )
		, m_style( new CTextStyle )
		, m_size(0,0)
		, m_arrangement( NULL )
		, m_count( -1 )
		, m_width( 0 )
		, m_height( 0 )
	{
	}

	// ~StyleTextBlock
	StyleTextBlock::~StyleTextBlock( void )
	{
		ClearLines();
	}

	// SetStyle
	void StyleTextBlock::SetStyle( Ref<CTextStyle> style )
	{
		if( m_style == style )
			return;

		// see if text needs to be relayouted
		bool need_layout = true;
		if( style.IsValid() && m_style.IsValid() )
		{
			if( !m_style->NeedRearrange( *style ) )
				need_layout = false;
		}
		if( need_layout )
		{
			ClearLines();
		}
		m_style = style;
	}

	// SetString
	void StyleTextBlock::SetString( const WString& str )
	{
		if( m_buffer == str )
			return;

		ClearLines();
		m_buffer = str;
	}

	// SetSize
	void StyleTextBlock::SetSize( const Pair<int>& size )
	{
		ClearLines();
		m_size = size;
	}

	// ClearLines
	void StyleTextBlock::ClearLines( void )
	{
		if( m_arrangement )
		{
			free( m_arrangement );
			m_arrangement = NULL;
		}
		m_count = -1;
		m_lines.clear();
		m_width = 0;
		m_height = 0;
	}

	// RelayoutText
	void StyleTextBlock::RelayoutText( void )
	{
		if( m_buffer.empty() )
			return;

		if( m_style.IsNull() )
		{
			// acquire parent's style.
			m_style = new CTextStyle();
		}

		m_count = (int)m_buffer.size();
		m_arrangement = (int*)malloc( m_count * sizeof(int) );

		int w = m_size.x;
		int h = m_size.y;

		// style.
		TextStyle& style = *m_style;
		PlacementStyle& placement = *m_style;

		int ascent = style.m_font->GetAscent();
		int descent = style.m_font->GetDescent();

		int pad_left = placement.m_padding_left == 0 ? placement.m_padding : placement.m_padding_left;
		int pad_top = placement.m_padding_top == 0 ? placement.m_padding : placement.m_padding_top;
		int pad_right = placement.m_padding_right == 0 ? placement.m_padding : placement.m_padding_right;
		int pad_bottom = placement.m_padding_bottom == 0 ? placement.m_padding : placement.m_padding_bottom;

		int text_height = style.m_font->GetFontHeight();
		int line_height = text_height + style.m_lineHeight;

		w -= pad_left + pad_right;
		h -= pad_top + pad_bottom;

		const wchar_t * ch = m_buffer.c_str();

		// Line by line.
		bool starter = true;
		int start_y = pad_top;
		wchar_t prev = 0;

		for( int i = 0 , total = m_buffer.size() ; i < total ; )
		{
			int ident = ( starter ? style.m_textIndent : 0 );
			int line_width = w - ident;

			const wchar_t * cc = ch + i;
			int * arrangement = m_arrangement + i;

			int word_width = 0;
			int count = style.ArrangeText( cc , -1 , arrangement , line_width , prev , &word_width , ident , true );

			if( count == 1 && cc[0] == '\n' )
			{
				// skip return only line
				if( !m_lines.empty() )
					m_lines.back().count += 1;
				prev = '\n';
				starter = true;
				i += 1;
				continue;
			}

			_Line l;

			l.arrangement = arrangement;
			l.count = count;
			l.startIndex = i;
			l.width = word_width;
			l.start_y = start_y;

			m_lines.push_back( l );

			// update info
			prev = cc[count-1];
			starter = ( prev == '\n' );
			start_y += line_height;
			m_width = max<int>( m_width , word_width );

			i += count;
		}

		m_height = m_lines.size() * line_height;
	}

	// DrawText
	void StyleTextBlock::DrawText( Composition& composition , const Rect<int>& place , const Rect<int>& bound , int offset /*= 0 */, int len /*= -1 */ )
	{
		if( m_buffer.size() == 0 )
			return;

		if( m_buffer.size() != m_count )
		{
			RelayoutText();
		}

		TextStyle& style = *m_style;
		PlacementStyle& placement = *m_style;

		int ascent = style.m_font->GetAscent();
		int descent = style.m_font->GetDescent();
		int text_height = style.m_font->GetFontHeight();
		int line_height = text_height + style.m_lineHeight;

		int pad_left = placement.m_padding_left == 0 ? placement.m_padding : placement.m_padding_left;
		int pad_top = placement.m_padding_top == 0 ? placement.m_padding : placement.m_padding_top;
		int pad_right = placement.m_padding_right == 0 ? placement.m_padding : placement.m_padding_right;
		int pad_bottom = placement.m_padding_bottom == 0 ? placement.m_padding : placement.m_padding_bottom;

		int total_width = pad_left + pad_right + m_width;
		int total_height = pad_top + pad_bottom + text_height;

		const wchar_t * cc = m_buffer.c_str();

		int head_y = place.top();

		// vertical alignment
		switch( placement.m_vertical )
		{
		case PlacementStyle::TOP:
			head_y += pad_top;
			break;

		case PlacementStyle::CENTER:
			head_y += place.height() / 2 - m_height / 2;
			break;

		case PlacementStyle::BOTTOM:
			head_y = place.bottom() - m_height - pad_bottom + descent;
			break;
		}

		wchar_t last = 0;

		for( size_t i = 0 , total = m_lines.size() ; i < total ; ++i )
		{
			_Line& l = m_lines[i];

			int start_x = place.left();
			int start_y = head_y + l.start_y;

			// horizontal alignment
			switch( placement.m_horizontal )
			{
			case PlacementStyle::LEFT:
				start_x += pad_left;
				break;

			case PlacementStyle::MIDDLE:
				start_x += place.width() / 2 - l.width / 2;
				break;

			case PlacementStyle::RIGHT:
				start_x = place.right() - l.width - pad_right;
				break;
			}

			start_x += style.m_textOffset.x;
			start_y += style.m_textOffset.y;

			// draw them by arrangement
			composition.painter().DrawText( style.m_textColor , style.m_font , cc + l.startIndex , l.count , bound ,
				Pair<int>( start_x , start_y ) , l.arrangement , &style , last );

			last = *(cc + l.startIndex + l.count-1);
		}
	}

} // namespace xeres
